package ss.pku.jsa.type.inference;

import ss.pku.jsa.type.constraint.solver.QError;

public class RefQType extends QType {
//	private static final int kind = 5;
	public boolean isRecursive = false;
	QType contents;
	public boolean isStatic;
	private FieldKey key;
	public boolean isGlobal = false;

	public RefQType(Location l, boolean original, QType contents,
			boolean isStatic) {
		this.name = (contents.name + "-ref");
		this.q = Qualifier.makeQualifierVariable(this.name, l, original);
		this.l = l;
		this.contents = contents;
		if ((contents instanceof RefQType)) {
			throw new IllegalArgumentException("Contents " + contents
					+ " is Ref at " + l);
		}
		this.original = original;
		this.isStatic = isStatic;
		this.type = (contents == null ? null : contents.type);
	}

	public RefQType copy(Location l, String name) {
		Profile.logMemStart("RefQType/copy/new RegQType");
		RefQType ret = new RefQType(l, false, deref() == null ? null : deref()
				.copy(l), this.isStatic);
		Profile.logMemEnd();
		ret.setKey(getKey());
		return ret;
	}

	public RefQType copy(Location l) {
		return copy(l, this.name);
	}

	public QType deref() {
		return getRefEcr().contents;
	}

	public void setContents(QType contents) {
		getRefEcr().contents = contents;
	}

	public boolean isPrimitive() {
		return false;
	}

	public int getKind() {
		return 5;
	}

	public void setOriginal(boolean original) {
		this.original = original;
		QType actualContents = deref();
		if (actualContents != null)
			actualContents.setOriginal(original);
	}

	public void setGlobal(boolean global) {
		this.q.setGlobal(global);
		this.isGlobal = global;
		QType actualContents = deref();
		if (actualContents != null)
			actualContents.setGlobal(global);
	}

	public void setPreferred(boolean preferred) {
		this.q.setPreferred(preferred);
		QType actualContents = deref();
		if (actualContents != null)
			actualContents.setPreferred(preferred);
	}

	public String toString() {
		return getName();
	}

	public void setKey(FieldKey key) {
		this.key = key;
	}

	public FieldKey getKey() {
		return this.key;
	}

	public RefQType getRefEcr() {
		return (RefQType) getEcr();
	}

	public void merge(QType q, Location loc, QError error) {
		if (!(q instanceof RefQType)) {
			throw new IllegalArgumentException("Merging non-ref QType " + q
					+ " with " + this);
		}

		CQual.makeEdge(Constraint.Kind.EQ, getQual(), q.getQual(),
				QualGate.EMPTY, loc, 0, error);

		RefQType qt = ((RefQType) q).getRefEcr();
		RefQType thisEcr = getRefEcr();

		if (equals(qt)) {
			return;
		}
		Profile.logMemStart("RefQType/merge/new MergeJob");
		MergeJob.addMergeJob(thisEcr.deref(), qt.deref(), loc, error);
		Profile.logMemEnd();

		this.ecr = qt;
	}

	protected void setName(String name) {
		super.setName(name + "_ref");
		QType contents = deref();
		if (contents != null)
			contents.setName(name);
	}

	public int getSize() {
		if (this.isRecursive)
			return 1;
		return deref().getSize();
	}

	public String toStringWithQuals(int fieldIndent) {
		StringBuilder ret = new StringBuilder();

		ret.append(getEcr().q.getInferred() + "ref ");

		if (this.isRecursive)
			ret.append(getRefEcr().contents.q.getInferred() + "recursive");
		else if (getRefEcr().contents != null) {
			ret.append(getRefEcr().contents.toStringWithQuals(fieldIndent));
		}
		return ret.toString();
	}
}
